public class PrinterThread implements Runnable{
  private Thread _myRunner;

  private float _myProgress = 0;
  private boolean _myIsPrinting = false;
  private boolean _myShouldPrint = false;

  private Serial _myPort;
  private PImage _myImage;

  private int COLS = 20;
  private int ROWS = 2;
  private int LINES = 0;
  private int BYTES = 640;

  private int[][] _myLines; 

  public PrinterThread(Serial thePort, PImage theImage){

    _myPort = thePort;
    _myImage = theImage;
    _myRunner = new Thread(this); 

    _myRunner.start();

  }

  void run(){
    while(true){
      if(_myShouldPrint){
        _myIsPrinting = true;

        processImage();
        sendImage();

        _myShouldPrint = false;
      }
      else {
        try{ 
          Thread.sleep(1000); 
        } 
        catch(Exception ex){ 
        };
      }
    }
  }

  void sendImage(){
    println("### Starting print ");

    for(int l = 0; l < LINES+5; l++){
      for(int i = 0; i< BYTES; i++){
        if(_myPort != null) _myPort.write(_myLines[l][i]); 
      } 

      _myProgress = (l)/(float)LINES;


      try{
        Thread.sleep(1800);  // TODO: Actually wait for printer
      } 
      catch(Exception ex){ 
        println(ex);
      }
    }

    try{
      Thread.sleep(1800);
    } 
    catch(Exception ex){ 
    }
    println("### Finished printing");
    _myProgress = 0;
    _myIsPrinting = false;
  }

  void processImage(){
    println("### Processing image ");

    LINES = _myImage.height / 16;
    _myLines = new int[LINES+5][BYTES];

    for(int l = 0; l < LINES; l++){
      int arrayIndex = 0;
      int length = COLS * ROWS * LINES;

      for(int r = 0; r<ROWS; r++){
        for(int c = 0; c<COLS; c++){

          for(int ir=0; ir<8; ir++){
            int bp1 = 0;
            int bp2 = 0;

            for(int ic=0; ic<8; ic++){
              color p = _myImage.get(c*8+ic, r*8+ir+l*16);
              if(red(p) <128) {
                bp1 += 1 << (7-ic);
              }
              bp2 = bp1;
            } 

            /* A single line in a tile (8x8) consists of two bit-planes */
            _myLines[l][arrayIndex ] = (int)bp1;
            arrayIndex++;
            _myLines[l][arrayIndex ] = (int)bp2;
            arrayIndex++;

          }
        } 
      }
    }
  }

  void startPrint(){
    _myShouldPrint = true;
  }

  float getProgress(){
    return  _myProgress; 
  }

  boolean isBusy(){
    return _myIsPrinting;
  }

}
